home *** CD-ROM | disk | FTP | other *** search
/ Multimedia Jumpstart / Multimedia Microsoft Jumpstart Version 1.1a (Microsoft).BIN / develpmt / sdk / vfw11.win / vfwdk / mcivisca.c_ / mcivisca.bin
Encoding:
Text File  |  1993-11-19  |  60.5 KB  |  1,895 lines

  1. /**************************************************************************
  2.  *
  3.  *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4.  *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5.  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6.  *  PURPOSE.
  7.  *
  8.  *  Copyright (c) 1992, 1993  Microsoft Corporation.  All Rights Reserved.
  9.  *
  10.  *  MCIVISCA.C
  11.  *
  12.  *  MCI ViSCA Device Driver
  13.  *
  14.  *  Description:
  15.  *
  16.  *      Main Module - Standard Driver Interface and Message Procedures
  17.  *                    DriverProc and DrvOpen and Close Routines.
  18.  *
  19.  *      *1)           mcivisca.c - DriverProc and DriverOpen and Close.
  20.  *       2)           mcicmds.c  - MCI commands.
  21.  *       3)           mcidelay.c - MCI delayed commands (asynchronous)
  22.  *       4)           viscamsg.c - ViSCA message construction procedures.
  23.  *       5)           viscacom.c - Comport procedures.
  24.  *       6)           commtask.c - Background task procedures.
  25.  *
  26.  *
  27.  * Warning: The scanning of system.ini for driver entries is not
  28.  *            recommended, but is done here because of the multiple
  29.  *            devices driver.
  30.  *
  31.  ***************************************************************************/
  32. #define  UNICODE
  33. #include <windows.h>
  34. #include <windowsx.h>
  35. #include "appport.h"
  36. #include <mmsystem.h>
  37. #include <mmddk.h>
  38. #include <string.h>
  39. #include <ctype.h>
  40. #include "vcr.h"
  41. #include "viscamsg.h"
  42. #include "viscadef.h"
  43. #include "mcivisca.h"
  44. #include "cnfgdlg.h"
  45. #include "common.h"            
  46.  
  47. static BOOL NEAR PASCAL viscaDlgUpdateNumDevs(HWND hDlg, int iPort, int iNumDevices);
  48. static BOOL NEAR PASCAL viscaDlgUpdatePort(HWND hDlg, int iPort);
  49. static BOOL NEAR PASCAL viscaDlgRead(HWND hDlg, int iPort);  // Port config to read things into.
  50. static int  NEAR PASCAL viscaComboGetPort(HWND hDlg);
  51. static int  NEAR PASCAL viscaDetectOnCommPort(int iPort);
  52.  
  53. /*
  54.  * A note about the shared memory.
  55.  *
  56.  * In the NT version all globals and instance structures are shared by
  57.  * using a shared memory block, which is allocated or mapped into a process
  58.  * space when a process attaches.
  59.  *
  60.  * In the WIN16 version the shared memory is just static data.
  61.  *
  62.  * Currently this imposes a maximum restriction on number of instances.
  63.  * Also the inter-process protection is not very robust.
  64.  */
  65.  
  66. #ifdef WIN32
  67. #pragma data_seg("MYSEG")
  68. #endif
  69. UINT uProcessCount = 0; //Must be initialized to 0.
  70.  
  71. #ifdef WIN32
  72. #pragma data_seg(".data")
  73. #endif
  74.  
  75. //
  76. // The following are per-instance pointers.
  77. //
  78. // This must be initialized each time this DLL maps into a process.
  79. UINT            uCommandTable = (UINT)MCI_NO_COMMAND_TABLE;   // handle to VCR command table
  80. HINSTANCE       hModuleInstance;    // module instance  (different in NT - DLL instances)
  81. POpenInstance   pinst;              // Pointer to use. (For both versions) NT it's per-instance.
  82. vcrTable        *pvcr;              // Pointer to use. (For both versions) NT it's per-instance.
  83. #ifdef WIN32
  84. HANDLE          hInstanceMap;       // per-instance map.
  85. HANDLE          hVcrMap;            // per-instance map.
  86. #endif
  87.  
  88. //
  89. // These are constants, so they don't change per-instance. (or you can share them safely).
  90. //
  91. CODESEGCHAR szNull[]                        = TEXT("");
  92. CODESEGCHAR szIni[]                         = TEXT("MCIVISCA");
  93. CODESEGCHAR szFreezeOnStep[]                = TEXT("FreezeOnStep");
  94. CODESEGCHAR sz1[]                           = TEXT("1");
  95. CODESEGCHAR sz0[]                           = TEXT("0");
  96. WCHAR szAllEntries[ALLENTRIES_LENGTH]; // Big enough for all entries in MCI section.
  97. WCHAR szKeyName[ONEENTRY_LENGTH];
  98.  
  99. /****************************************************************************
  100.  * Function: BOOL MemInitializeVcrTable - Initialize global variables (now in structure).
  101.  *
  102.  * Returns: TRUE
  103.  *
  104.  ***************************************************************************/
  105. BOOL MemInitializeVcrTable(void)
  106. {
  107.     int iPort;
  108.     //
  109.     // All globals defined and initialized here 
  110.     //
  111.     uCommandTable                 = (UINT)MCI_NO_COMMAND_TABLE;   // handle to VCR command table
  112.     pvcr->gfFreezeOnStep          = FALSE;
  113.     pvcr->htaskCommNotifyHandler  = 0;
  114.     pvcr->uTaskState              = TASKINIT;
  115.     pvcr->lParam                  = 0;
  116.     pvcr->hwndCommNotifyHandler   = (HWND) 0;
  117.     pvcr->gfTaskLock              = FALSE;
  118. #ifdef DEBUG
  119.     pvcr->iGlobalDebugMask        = DBGMASK_CURRENT;        //see common.h
  120. #endif
  121.     // Set all port IDs to -1, since 0 is a valid port ID.
  122.     //
  123.     for (iPort = 0; iPort < MAXPORTS; iPort++)
  124.     {
  125.         pvcr->Port[iPort].idComDev = BAD_COMM;
  126.         pvcr->Port[iPort].nUsage   = 0;
  127.     }
  128.  
  129.  
  130.     DPF(DBG_MEM, "InitializeVcrTable - completed succesfully");
  131.  
  132.     return TRUE;
  133. }
  134.  
  135.  
  136.  
  137. /****************************************************************************
  138.  * Function: BOOL MemInitializeInstances - Initialize heap of instances.
  139.  *
  140.  * Returns: TRUE
  141.  *
  142.  ***************************************************************************/
  143. BOOL MemInitializeInstances(void)
  144. {
  145.     int i;
  146.  
  147.     // Erase any old data.
  148.     _fmemset(pinst, (BYTE)0, sizeof(OpenInstance) * MAX_INSTANCES);
  149.  
  150.     // (Redundant) Set all In use flags to false.
  151.     for(i = 0; i < MAX_INSTANCES; i++)
  152.         pinst[i].fInUse = FALSE;
  153.  
  154.     DPF(DBG_MEM, "InitializeInstances - completed successfully");
  155.     return TRUE;
  156. }
  157.  
  158.  
  159. /****************************************************************************
  160.  * Function: BOOL MemAllocInstance - Allocate one instance from heap of instances.
  161.  *
  162.  * Returns: TRUE
  163.  *
  164.  ***************************************************************************/
  165. int MemAllocInstance(void)  // Return an offset.
  166. {
  167.     int i;
  168.  
  169.     for(i = 0; i < MAX_INSTANCES; i++)
  170.     {
  171.         if(!pinst[i].fInUse)
  172.             break;
  173.     }
  174.     if(i == MAX_INSTANCES)
  175.         return 0;
  176.  
  177.     DPF(DBG_MEM, "MemAllocInstance - instance %x \n", i);
  178.  
  179.     pinst[i].fInUse = TRUE;
  180.  
  181.     // Use offsets only, so return
  182.  
  183.     return i;
  184. }
  185.  
  186. /****************************************************************************
  187.  * Function: BOOL MemFreeInstance - Free the instance, return it to heap of instances.
  188.  *
  189.  * Parameters:
  190.  *
  191.  *      int  iInstance       - Instance to free.
  192.  *
  193.  * Returns: TRUE
  194.  *
  195.  ***************************************************************************/
  196. BOOL MemFreeInstance(int iInstance)
  197. {
  198.     _fmemset(&pinst[iInstance], (BYTE)0, sizeof(OpenInstance));
  199.     pinst[iInstance].fInUse = FALSE;
  200.  
  201.     DPF(DBG_MEM, "MemFreeInstance - instance %d \n", iInstance);
  202.     return TRUE;
  203. }
  204.  
  205.  
  206. /****************************************************************************
  207.  * Function: BOOL IsSpace - WIN32/16 compatible version of _isspace.
  208.  *
  209.  * Parameters:
  210.  *
  211.  *      WCHAR wchTest - character or wide character to test.
  212.  *
  213.  * Returns: TRUE if it is white character
  214.  *
  215.  ***************************************************************************/
  216. BOOL IsSpace(WCHAR wchTest)
  217. {
  218.     if( (wchTest == TEXT(' ')) || (wchTest == TEXT('\t')) )
  219.         return TRUE;
  220.     else
  221.         return FALSE;
  222.  
  223. }
  224.  
  225. /****************************************************************************
  226.  * Function: BOOL IsDigit - WIN32/16 compatible version of _isdigit.
  227.  *
  228.  * Returns: TRUE if it is a digit (0-9)
  229.  *
  230.  ***************************************************************************/
  231. BOOL IsDigit(WCHAR wchTest)
  232. {
  233.  
  234.     if( (wchTest >= TEXT('0')) && (wchTest <= TEXT('9')) )
  235.         return TRUE;
  236.     else
  237.         return FALSE;
  238.  
  239. }
  240. /****************************************************************************
  241.  * Function: BOOL IsAlpha - Is it an alphabetical character.
  242.  *
  243.  * Returns: TRUE if it is alpha (A-Z, a-z)
  244.  *
  245.  ***************************************************************************/
  246. BOOL IsAlpha(WCHAR wchTest)
  247. {
  248.     if( ((wchTest >= TEXT('A')) && (wchTest <= TEXT('Z'))) ||
  249.         ((wchTest >= TEXT('a')) && (wchTest <= TEXT('z'))) )
  250.         return TRUE;
  251.     else
  252.         return FALSE;
  253.  
  254. }
  255. /****************************************************************************
  256.  * Function: BOOL IsAlphaNumeric - Alphabetic or numeric.
  257.  *
  258.  * Returns: TRUE if alpha or numeric.
  259.  *
  260.  ***************************************************************************/
  261. BOOL IsAlphaNumeric(WCHAR wchTest)
  262. {
  263.     if(IsDigit(wchTest))
  264.         return TRUE;
  265.  
  266.     if(IsAlpha(wchTest))
  267.         return TRUE;
  268.  
  269.     return FALSE;
  270. }
  271.  
  272.  
  273. #ifdef WIN32
  274. int APIENTRY DLLEntryPoint(PVOID hModule, ULONG Reason, PCONTEXT pContext);
  275.  
  276. /****************************************************************************
  277.  * Function: int DLLEntryPoint - Each process and thread that attaches causes this to be called.
  278.  *
  279.  * Parameters:
  280.  *
  281.  *      PVOID hModule - This instance of the DLL. (each process has own).
  282.  *
  283.  *      ULONG Reason - Reason | Reason for attaching. (thread or process).
  284.  *
  285.  *      PCONTEXT pContext - I don't know?
  286.  *
  287.  * Returns: TRUE
  288.  *
  289.  ***************************************************************************/
  290. int APIENTRY DLLEntryPoint(PVOID hModule, ULONG Reason, PCONTEXT pContext)
  291. {
  292.     BOOL fInitSharedMem, fIgnore;
  293.  
  294.     if (Reason == DLL_PROCESS_ATTACH)
  295.     {
  296.         /* Create the vcr area - This includes globals used for debugging
  297.          * So it MUST be done before we allocate for the instances.
  298.          */
  299.  
  300.         hVcrMap = CreateFileMapping((HANDLE) 0xffffffff, NULL,
  301.             PAGE_READWRITE, 0, sizeof(vcrTable),
  302.             TEXT("mciviscaVcrTable"));
  303.  
  304.         if(hVcrMap == NULL)
  305.             return 0;
  306.  
  307.         fInitSharedMem = (GetLastError() != ERROR_ALREADY_EXISTS);
  308.  
  309.         pvcr = (vcrTable *) MapViewOfFile(hVcrMap, FILE_MAP_WRITE, 0, 0, 0);
  310.  
  311.         if(pvcr == NULL)
  312.             return 0;
  313.  
  314.         /* Initialize the vcr table, set this before calling the thing. */
  315.  
  316.         hModuleInstance = hModule;
  317.         if(fInitSharedMem)
  318.             MemInitializeVcrTable();
  319.  
  320.         /* Create the instance storage area */
  321.  
  322.         hInstanceMap = CreateFileMapping((HANDLE) 0xffffffff, NULL,
  323.             PAGE_READWRITE, 0, sizeof(OpenInstance) * MAX_INSTANCES,
  324.             TEXT("mciviscaInstanceMap"));
  325.  
  326.         if(hInstanceMap == NULL)
  327.             return 0;
  328.  
  329.         fInitSharedMem = (GetLastError() != ERROR_ALREADY_EXISTS);
  330.  
  331.         pinst = (POpenInstance) MapViewOfFile(hInstanceMap, FILE_MAP_WRITE, 0, 0, 0);
  332.  
  333.         if(pinst == NULL)
  334.             return 0;
  335.  
  336.         /* Initialize the instance area if this is the first time. */
  337.         if(fInitSharedMem)
  338.             MemInitializeInstances();
  339.  
  340.  
  341.     } else
  342.     {
  343.         if (Reason == DLL_PROCESS_DETACH)
  344.         {
  345.             if(pinst != NULL)
  346.                 fIgnore = UnmapViewOfFile(pinst);
  347.  
  348.             if(hInstanceMap != NULL)
  349.                 fIgnore = CloseHandle(hInstanceMap);
  350.  
  351.             if(pvcr  != NULL)
  352.                 fIgnore = UnmapViewOfFile(pvcr);
  353.  
  354.             if(hVcrMap != NULL)
  355.                 fIgnore = CloseHandle(hVcrMap);
  356.  
  357.  
  358.         }
  359.  
  360.     }
  361.     return TRUE;
  362. }
  363. #else
  364. /****************************************************************************
  365.  * Function: int LibMain - Library initialization code.
  366.  *
  367.  * Parameters:
  368.  *
  369.  *      HINSTANCE hModInst - Library instance handle.
  370.  *
  371.  *      WORD wDataSeg - Data segment.
  372.  *
  373.  *      WORD cbHeapSize - Heap size.
  374.  *
  375.  *      LPSTR lpszCmdLine - The command line.
  376.  *
  377.  * Returns: 1 if the initialization was successful and 0 otherwise.
  378.  ***************************************************************************/
  379. int FAR PASCAL
  380. LibMain(HINSTANCE hModInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
  381. {
  382.     hModuleInstance = hModInst;
  383.     return (1);
  384. }
  385. #endif
  386.  
  387. /*
  388.  *      WIN16 - makes global pointer point to static data.
  389.  */
  390. #ifndef WIN32
  391. //
  392. // In Win3.1 the static variables are allocated here.
  393. //
  394. OpenInstance arRealInst[MAX_INSTANCES];     // The real non-aliased thing.
  395. vcrTable     vcrReal;                       // The real non-aliased thing.
  396.  
  397. OpenInstance *MemCreateInstances(void)
  398. {
  399.     pinst =  &arRealInst[0];
  400.     return pinst;
  401. }
  402.  
  403. vcrTable *MemCreateVcrTable(void)
  404. {
  405.     pvcr  =  &vcrReal;
  406.     return pvcr;
  407. }
  408. #endif
  409.  
  410.  
  411. /****************************************************************************
  412.  * Function: LRESULT viscaDrvLoad - Respond to the DRV_LOAD message.
  413.  *                 Perform any one-time initialization.
  414.  *
  415.  * Returns: TRUE on success and FALSE on failure.
  416.  ***************************************************************************/
  417. static LRESULT NEAR PASCAL
  418. viscaDrvLoad(void)
  419. {
  420.     // In WIN16 this shouldn't make a difference since this only gets called once.
  421.     uProcessCount++; //This is the only shared thing!
  422.    
  423. #ifdef WIN32 
  424.     // In NT we do our own shareable counting.
  425.     // The first time we enter this our process count will be 1.
  426.     if(uProcessCount > 1)
  427.         return ((LRESULT)TRUE);
  428. #else
  429.     // In NT version this is all done in the attach detach section of DLLEntry.
  430.     MemCreateInstances();        // This is only once.
  431.     MemCreateVcrTable();         // In NT maps everything and returns a pointer to mem-map
  432. #endif
  433.  
  434. #ifndef WIN32
  435.     //
  436.     // You must always use YOUR view of this memory in all functions.
  437.     //
  438.     MemInitializeVcrTable();        // In NT every process will AUTOMAGICALLY have its own handle.
  439.     MemInitializeInstances();       // Because the globals will be on a per-instance basis.
  440. #endif
  441.     // Alloc the auto-instance pointer-flag for all now.
  442.     pvcr->iInstBackground = MemAllocInstance();
  443.  
  444.     DPF(DBG_MEM, "viscaDrvLoad - initalized table and instances.");
  445.  
  446.     return ((LRESULT)TRUE);
  447. }
  448.  
  449.  
  450. /****************************************************************************
  451.  * Function: LRESULT viscaDrvClose - Respond to the DRV_CLOSE message.  Perform
  452.  *     any cleanup necessary each time the device is closed.
  453.  *
  454.  * Parameters:
  455.  *
  456.  *     WORD wDeviceID - The device id being closed.
  457.  *
  458.  * Returns: TRUE on success and FALSE on failure.
  459.  ***************************************************************************/
  460. static LRESULT NEAR PASCAL
  461. viscaDrvClose(WORD wDeviceID)
  462. {
  463.     int iInst   = (int)mciGetDriverData(wDeviceID);
  464.  
  465.      // This cannot be 0
  466.     if(iInst != 0)
  467.         viscaInstanceDestroy(iInst);
  468.  
  469.     DPF(DBG_COMM, "viscaDrvClose - completed \n");
  470.  
  471.     return ((LRESULT)TRUE);
  472. }
  473.  
  474.  
  475. /****************************************************************************
  476.  * Function: LRESULT viscaDrvFree - Respond to the DRV_FREE message.
  477.  *                 Perform any device shutdown tasks.
  478.  *
  479.  * Returns: TRUE on success and FALSE on failure.
  480.  ***************************************************************************/
  481. static LRESULT NEAR PASCAL
  482. viscaDrvFree(WORD wDeviceID)
  483. {
  484.     int i;
  485.     int iCount = 0;
  486.  
  487.     // In NT I assume we get this immediately before our DLLEntry gets
  488.     // called with the process detach message.
  489.  
  490.     uProcessCount--; //This is the only shared thing!
  491.  
  492.     // If a command table is loaded, then free it.
  493.     if (uCommandTable != MCI_NO_COMMAND_TABLE)
  494.     {
  495.         DPF(DBG_MEM, "Freeing table=%u", uCommandTable);
  496.         mciFreeCommandResource(uCommandTable);
  497.         uCommandTable = (UINT)MCI_NO_COMMAND_TABLE;
  498.     }
  499.  
  500.     if(uProcessCount > 1)
  501.     {
  502.         // In NT this allows us to maintain accross multiple DrvFree messages.
  503.         DPF(DBG_ERROR, "DrvFree: uProcessCount > 1, uProcessCount=%u", uProcessCount);
  504.         return ((LRESULT)TRUE);
  505.     }
  506.  
  507.     for(i = 0; i < MAX_INSTANCES; i++)
  508.     {
  509.         if(pinst[i].fInUse)
  510.             iCount++;
  511.     }
  512.  
  513.     DPF(DBG_MEM, "DrvFree number of instances=%u", iCount);
  514.  
  515.     if(iCount > 1) // Auto inst is one.
  516.     {
  517.         // Just ignore this message.
  518.         DPF(DBG_ERROR, "DrvFree: Instances != 1, i=%u",iCount);
  519.         return ((LRESULT)TRUE);
  520.     }
  521.  
  522.     // If there's a background task, then destroy it.
  523.     //
  524.     // Free the global auto-inst.
  525.     //
  526.     MemFreeInstance(pvcr->iInstBackground); //Map goes at exit time.
  527.  
  528.     if (viscaTaskIsRunning())
  529.     {
  530.         viscaTaskDestroy();
  531.     }
  532.  
  533.     return ((LRESULT)TRUE);
  534. }
  535.  
  536.  
  537. /****************************************************************************
  538.  * Function: LPSTR SkipWord - Skips first word in a string up to the second word.
  539.  *
  540.  * Parameters:
  541.  *
  542.  *      LPWSTR lpcsz - String to parse.
  543.  *
  544.  * Returns: pointer to first character in second word.
  545.  ***************************************************************************/
  546. static LPWSTR NEAR PASCAL
  547.     SkipWord(LPWSTR lpsz)
  548. {
  549.     while ((*lpsz) && !IsSpace(*lpsz))
  550.         lpsz++;
  551.  
  552.     while(IsSpace(*lpsz))
  553.         lpsz++;
  554.  
  555.     return (lpsz);
  556. }
  557.  
  558.  
  559. /****************************************************************************
  560.  * Function: void ParseParams - Parse port & dev. nos. from a string like "2 1".
  561.  *
  562.  * Parameters:
  563.  *
  564.  *      LPCSTR lpstrParams - String to parse.
  565.  *
  566.  *      UINT FAR * lpnPort - Port # to be filled in (1..4).
  567.  *
  568.  *      UINT FAR * lpnDevice - Device # to be filled in (1..7).
  569.  ***************************************************************************/
  570. static void NEAR PASCAL
  571. ParseParams(LPCWSTR lpstrParams, UINT FAR * lpnPort, UINT FAR * lpnDevice)
  572. {
  573.     UINT    nPort   = DEFAULTPORT;
  574.     UINT    nDevice = DEFAULTDEVICE;
  575.  
  576.     // Find first digit -- Port #
  577.     while ((*lpstrParams) && (!IsDigit(*lpstrParams)))
  578.         lpstrParams++;
  579.  
  580.     if (*lpstrParams != TEXT('\0'))
  581.     {
  582.         nPort = (*lpstrParams) - TEXT('0');
  583.         lpstrParams++;
  584.         // Find second digit -- Device #
  585.         while ((*lpstrParams) && (!IsDigit(*lpstrParams)))
  586.             lpstrParams++;
  587.  
  588.         if (*lpstrParams != TEXT('\0'))
  589.             nDevice = (*lpstrParams) - TEXT('0');
  590.     }
  591.  
  592.     if (INRANGE(nPort, 1, MAXPORTS))
  593.         *lpnPort = nPort;
  594.     else
  595.         *lpnPort = DEFAULTPORT;
  596.  
  597.     if (INRANGE(nDevice, 1, MAXDEVICES))
  598.         *lpnDevice = nDevice;
  599.     else 
  600.         *lpnDevice = DEFAULTDEVICE;
  601. }
  602.  
  603.  
  604. /****************************************************************************
  605.  * Function: LRESULT viscaDrvOpen - Respond to the DRV_OPEN message.  Perform any
  606.  *     initialization which is done once each time the driver is opened.
  607.  *
  608.  * Parameters:
  609.  *
  610.  *      LPWSTR lpstrParams - NULL terminated command line string contains
  611.  *     any characters following the filename in the SYSTEM.INI file.
  612.  *
  613.  *      LPMCI_OPEN_DRIVER_PARMS lpOpen - Pointer to an
  614.  *     MCI_OPEN_DRIVER_PARMS structure with information about this device.
  615.  *
  616.  * Returns: zero on failure or the driver ID that should be passed
  617.  *      to identify this device on subsequent messages.
  618.  *
  619.  *     In this driver, the DRV_OPEN message is used to parse the
  620.  *     parameters string and fill in the device type and custom command
  621.  *     table.  The OPEN_DRIVER message is MCI specific and is used to
  622.  *     register this device with the sample device.
  623.  ***************************************************************************/
  624. static LRESULT NEAR PASCAL
  625. viscaDrvOpen(LPWSTR lpstrParams, MCI_OPEN_DRIVER_PARMS FAR * lpOpen)
  626. {
  627.     UINT                nPort;
  628.     UINT                nDevice;
  629.     int                 iInst;
  630.  
  631.     // Find port and device # to use
  632.     ParseParams(lpOpen->lpstrParams, &nPort, &nDevice);
  633.  
  634.     nPort--;
  635.     nDevice--;
  636.  
  637.     if((nPort >= MAXPORTS) || (nDevice >= MAXDEVICES))
  638.         return 0L;
  639.  
  640.     // Create each instances thing.
  641.     iInst = viscaInstanceCreate(lpOpen->wDeviceID, nPort, nDevice);
  642.  
  643.     if (iInst == -1) 
  644.         return (0L);
  645.  
  646.  
  647.     // If this is the first device to be openned with this driver,
  648.     // then start backgound task and load VCR-specific command table.
  649.     if (!viscaTaskIsRunning())
  650.     {
  651.         WCHAR    szTableName[16];
  652.         
  653.         if(LoadString(hModuleInstance, IDS_TABLE_NAME, szTableName, sizeof(szTableName) / sizeof(WCHAR)))
  654.         {
  655.             uCommandTable = mciLoadCommandResource(hModuleInstance, szTableName, 0);
  656.  
  657.             if(uCommandTable == MCI_NO_COMMAND_TABLE)
  658.             {
  659.                 DPF(DBG_ERROR, "Failed to load command table\n");
  660.                 return 0L;  // Fail the load
  661.             }
  662.  
  663.             DPF(DBG_MEM, "Table num=%u \n",uCommandTable);
  664.         }
  665.  
  666.         if (!viscaTaskCreate())
  667.         {
  668.             DPF(DBG_ERROR, "Failed to create task.\n");
  669.             DPF(DBG_MEM, "viscaInstanceDestroy - Freeing iInst = %d \n", iInst);
  670.             return (0L);
  671.         }
  672.  
  673.     }
  674. #ifdef WIN32
  675.     else
  676.     {
  677.         WCHAR    szTableName[16];
  678.  
  679.         if(uCommandTable == MCI_NO_COMMAND_TABLE)
  680.         {
  681.             if(LoadString(hModuleInstance, IDS_TABLE_NAME, szTableName, sizeof(szTableName) / sizeof(WCHAR)))
  682.             {
  683.                 uCommandTable = mciLoadCommandResource(hModuleInstance, szTableName, 0);
  684.  
  685.                 if(uCommandTable == MCI_NO_COMMAND_TABLE)
  686.                 {
  687.                     DPF(DBG_ERROR, "Failed to load command table\n");
  688.                     return 0L;  // Fail the load
  689.                 }
  690.                 DPF(DBG_MEM, "Table num=%u \n",uCommandTable);
  691.             }
  692.         }
  693.     }
  694. #endif
  695.  
  696.     // Fill in return information
  697.     lpOpen->wCustomCommandTable = uCommandTable;
  698.     lpOpen->wType = MCI_DEVTYPE_VCR;   // It will now search vcr.mci for strings. (as default).
  699.  
  700.     // Kludge for EVO-9650 - forces freeze on every step if using vfw 1.0
  701.     if (GetProfileInt(szIni, (LPWSTR) szFreezeOnStep, 0))
  702.         pvcr->gfFreezeOnStep = TRUE;
  703.     else
  704.         pvcr->gfFreezeOnStep = FALSE;
  705.  
  706.     DPF(DBG_COMM, "viscaDrvOpen - completed \n");
  707.  
  708.     /* this return value will be passed in as dwDriverID in future calls. */
  709.     return (lpOpen->wDeviceID);
  710. }
  711.  
  712. /****************************************************************************
  713.  * Function: void GetCmdParams - Read port & device nos. from system.ini.
  714.  *
  715.  * Parameters:
  716.  *
  717.  *      LPDRVCONFIGINFO lpdci - Pointer to driver configuration struct.
  718.  *
  719.  *      UINT FAR * lpnPort - Port # to be filled in (1..4).
  720.  *
  721.  *      UINT FAR * lpnDevice - Device # to be filled in (1..7).
  722.  ***************************************************************************/
  723. static void NEAR PASCAL
  724. GetCmdParams(LPDRVCONFIGINFO lpdci, UINT FAR * lpnPort, UINT FAR * lpnDevice)
  725. {
  726.     WCHAR    szIniFile[FILENAME_LENGTH];
  727.     WCHAR    szParams[MAX_INI_LENGTH];
  728.  
  729.     if (LoadString(hModuleInstance, IDS_INIFILE, szIniFile, sizeof(szIniFile)) &&
  730.         GetPrivateProfileString(lpdci->lpszDCISectionName,
  731.                                 lpdci->lpszDCIAliasName, szNull, szParams,
  732.                                 MAX_INI_LENGTH, szIniFile))
  733.         ParseParams(SkipWord(szParams), lpnPort, lpnDevice);
  734.     else
  735.         ParseParams(szNull, lpnPort, lpnDevice);
  736.  
  737. }
  738.  
  739. /****************************************************************************
  740.  * Function: BOOL viscaWriteAllVcrs - Write all configuration to system.ini
  741.  *
  742.  * Parameters:
  743.  *
  744.  *      LPCSTR lpszSectionName - Should be [mci] for Windows 3.1
  745.  *
  746.  * Returns: TRUE if successful.
  747.  *
  748.  ***************************************************************************/
  749. static BOOL NEAR PASCAL
  750. viscaWriteAllVcrs(LPCWSTR lpszSectionName)
  751. {
  752.     int     iPort, iDev;
  753.     WCHAR   sz[MAX_INI_LENGTH];
  754.     WCHAR   szIniFile[FILENAME_LENGTH];
  755.     WCHAR   szVersionName[FILENAME_LENGTH];
  756.  
  757.     DPF(DBG_CONFIG, "viscaWriteAllVcrs\n");
  758.  
  759.     if(!LoadString(hModuleInstance, IDS_VERSIONNAME, szVersionName, sizeof(szVersionName)))
  760.         return FALSE;
  761.  
  762.     if (!LoadString(hModuleInstance, IDS_INIFILE, szIniFile, sizeof(szIniFile)))
  763.         return FALSE;
  764.     
  765.     for (iPort = 0; iPort < MAXPORTS; iPort++)
  766.     {
  767.         for(iDev = 0; iDev < MAXDEVICES; iDev++)
  768.         {
  769.             if(pvcr->Port[iPort].Dev[iDev].szVcrName[0] != TEXT('\0'))
  770.             {
  771.                 wsprintf((LPWSTR)sz, TEXT("%s com%u %u"), (LPWSTR)szVersionName, iPort+1, iDev+1);
  772.  
  773.                 DPF(DBG_CONFIG, "Writing to ini file=%s\n", (LPWSTR)sz);
  774.  
  775.                 WritePrivateProfileString((LPCTSTR)lpszSectionName,
  776.                                     pvcr->Port[iPort].Dev[iDev].szVcrName,                                
  777.                                     sz,
  778.                                     szIniFile);
  779.             }
  780.         }
  781.     }
  782.     //
  783.     // Write the EVO-9650 kludge out. FreezeOnStep for backwards compatability.
  784.     //
  785.     WriteProfileString(szIni, szFreezeOnStep, 
  786.         (pvcr->gfFreezeOnStep) ? sz1 : sz0);
  787.  
  788.     return TRUE;
  789. }
  790.  
  791.  
  792.  
  793. /****************************************************************************
  794.  * Function: BOOL viscaAllVcrs - Read or delete all configuration to system.ini
  795.  *
  796.  * Parameters:
  797.  *
  798.  *      LPCSTR lpszSectionName - Should be [mci] for Windows 3.1
  799.  *
  800.  *      BOOL fDelete - If TRUE delete all mcivisca, else read all configuration.
  801.  *
  802.  * Returns: TRUE if successful.
  803.  *
  804.  *      
  805.  *
  806.  * This function is called to either:
  807.  *      1. Get the current state of all mcivisca.drv
  808.  *      2. Delete all mcivisca.drv entries in system.ini
  809.  *
  810.  *    0. Get all keys in mci section.
  811.  *         1. Get key
  812.  *         2. Get value
  813.  *         3. if first string in value == mcivisca then 
  814.  *             1. get comm number
  815.  *             2. get dev number
  816.  *             3. write key at comm, dev.
  817.  *   
  818.  *    1. Then done with all keys. (see viscaUpdatePort)
  819.  *         loop for all commports.
  820.  *         find max string.
  821.  *         if there are any holes 
  822.  *             make a name (vcrn) (add total of vcr's on each commport).
  823.  *             check no-one else uses it.
  824.  *
  825.  ***************************************************************************/
  826. static BOOL NEAR PASCAL
  827. viscaAllVcrs(LPCWSTR lpszSectionName, BOOL fDelete)
  828. {
  829.     int     iPort, iDev;
  830.     LPWSTR  pchEntry, pchParams, pchOneString; // Pointer to step through list of entries.
  831.     WCHAR   szOneEntry[ONEENTRY_LENGTH];     // Name of one entry.  (LHS)
  832.     WCHAR   szOneString[ONEENTRY_LENGTH];    // String for an entry (RHS)
  833.     WCHAR   szVersionName[FILENAME_LENGTH];
  834.     WCHAR   szIniFile[FILENAME_LENGTH];
  835.     int     i=0;
  836.  
  837.     if(!LoadString(hModuleInstance, IDS_INIFILE, szIniFile, sizeof(szIniFile)))
  838.         return FALSE;
  839.  
  840.     if(!LoadString(hModuleInstance, IDS_VERSIONNAME, szVersionName, FILENAME_LENGTH))
  841.         return FALSE;
  842.  
  843.     DPF(DBG_CONFIG, "VersionName=%s\n", (LPWSTR)szVersionName);
  844.     //
  845.     // Get all entry keys. (check for fail!)
  846.     //
  847.     GetPrivateProfileString((LPCTSTR)lpszSectionName,
  848.                            NULL, szNull, szAllEntries,
  849.                            ALLENTRIES_LENGTH, szIniFile);
  850.  
  851.     pchEntry = szAllEntries;
  852.  
  853.     while(*pchEntry)
  854.     {
  855.         //
  856.         // Get one entry name.
  857.         //
  858.         for(i = 0; *pchEntry != TEXT('\0'); pchEntry++, i++)
  859.             szOneEntry[i] = *pchEntry;
  860.         szOneEntry[i] = TEXT('\0'); // Null terminate it. 
  861.         //
  862.         // Get the profile string for this entry.
  863.         //
  864.         GetPrivateProfileString((LPCTSTR)lpszSectionName,
  865.                            szOneEntry, szNull, szOneString,
  866.                            ONEENTRY_LENGTH, szIniFile);
  867.         //
  868.         // Skip any leading spaces.
  869.         //
  870.         pchOneString = szOneString;
  871.         while(*pchOneString && IsSpace(*pchOneString))
  872.             pchOneString++;
  873.         //
  874.         // Strip the first word (upto space or null terminated).
  875.         //
  876.         for(i=0; pchOneString[i] && !IsSpace(pchOneString[i]); i++);
  877.         if(pchOneString[i])
  878.             pchOneString[i] = TEXT('\0'); // Null terminate the thing.
  879.  
  880.         pchParams = &(pchOneString[i+1]); //Always work with arrays instead of ptrs!
  881.                                           //That way it is portable to NT.
  882.         //
  883.         // Is szOneString==mcivisca.drv?
  884.         //
  885.         if(lstrcmpi(pchOneString, szVersionName)==0)
  886.         {
  887.             if(fDelete)
  888.             {
  889.                 // Yes it is. So delete it!
  890.                 WritePrivateProfileString((LPCTSTR)lpszSectionName,
  891.                             szOneEntry,
  892.                             NULL,   //NULL means DELETE!
  893.                             szIniFile);
  894.             }
  895.             else
  896.             {
  897.                 DPF(DBG_CONFIG, "OneEntry == mcivisca.drv\n");
  898.  
  899.                 // Get pchParams pointing to first valid char of command line.
  900.                 while(IsSpace(*pchParams))
  901.                     pchParams++;
  902.  
  903.                 ParseParams(pchParams, &iPort, &iDev);
  904.                 iPort--;
  905.                 iDev--;
  906.                 DPF(DBG_CONFIG, "Port=%d Device=%d\n", iPort, iDev);
  907.                 //
  908.                 // Now store the name (the entry) at the default location.
  909.                 //
  910.                 if((iPort < MAXPORTS) && (iDev < MAXDEVICES))
  911.                     lstrcpy(pvcr->Port[iPort].Dev[iDev].szVcrName, szOneEntry);
  912.             }
  913.         }
  914.         else
  915.         {
  916.             DPF(DBG_CONFIG, "Entry=%s", (LPWSTR)szOneEntry);
  917.         }
  918.         //
  919.         // Skip junk, and get to the next one.
  920.         //
  921.         while(*pchEntry != TEXT('\0'))
  922.             pchEntry++;
  923.         //
  924.         // Ok, the next character is either null (which means the end)
  925.         // or it is a valid character.
  926.         //
  927.         pchEntry++;
  928.     }
  929.  
  930. }
  931.  
  932.  
  933. /****************************************************************************
  934.  * Function: BOOL viscaDlgUpdatePort - Called every time the commport in the configuration
  935.  *          dialog is changed.
  936.  *
  937.  * Parameters:
  938.  *
  939.  *      HWND hDlg - Configuration dialog window.
  940.  *
  941.  *      int  iPort - index into vcr array corresponding to commport (commport-1).
  942.  *
  943.  * Returns: TRUE if successful.
  944.  *
  945.  ***************************************************************************/
  946. static BOOL NEAR PASCAL
  947. viscaDlgUpdatePort(HWND hDlg, int iPort)  // Sends new commport.
  948. {
  949.     int  iDev;
  950.     int  iNumDevices    = 0;
  951.     HWND hComboPort     = GetDlgItem(hDlg, IDD_COMBO_PORT);
  952.     HWND hComboDevice   = GetDlgItem(hDlg, IDD_COMBO_DEVICE);
  953.     int  i              = 0;
  954.     int  iIndexCombo    = 0;
  955.  
  956.     DPF(DBG_CONFIG, "viscaDlgUpdatePort - setting port to %d\n", iPort);
  957.  
  958.     //
  959.     // Reduce the index by the number of ports that don't exist
  960.     //
  961.     for(i=0; i<iPort; i++)
  962.         if(pvcr->Port[i].fExists)
  963.             iIndexCombo++;
  964.  
  965.     // Make the current settings the default
  966.     ComboBox_SetCurSel(hComboPort, iIndexCombo); // This is the 0 relative one. (so 0==>com1)
  967.     //
  968.     // Get the number of devices on this serial port. (0-7) for a total num of devs.
  969.     //
  970.     for(iDev=0; iDev < MAXDEVICES; iDev++)
  971.     {
  972.         if(pvcr->Port[iPort].Dev[iDev].szVcrName[0] != TEXT('\0'))
  973.             iNumDevices = iDev + 1;
  974.     }
  975.  
  976.     DPF(DBG_CONFIG, "viscaDlgUpdatePort - setting number of devs to %d\n", iNumDevices);
  977.  
  978.     ComboBox_SetCurSel(hComboDevice, iNumDevices); // 0==>0, 1==>1, etc.
  979.     pvcr->iLastNumDevs = iNumDevices;  // We assume the last is saved already on a port change.
  980.     //
  981.     // Now tell viscaDlgUpdateNumDevs to fill in the number of devs we have listed.
  982.     //
  983.     viscaDlgUpdateNumDevs(hDlg, iPort, iNumDevices);
  984.  
  985.     return TRUE;
  986. }
  987.  
  988.  
  989. /****************************************************************************
  990.  * Function: BOOL MakeMeAGoodName - Make a name up for a vcr. Make sure it doesn't
  991.  *      already exist in the vcr array or in the configuration dialog.
  992.  *
  993.  * Parameters:
  994.  *
  995.  *      HWND hDlg - Configuration dialog.
  996.  *
  997.  *      int  iPort - index into vcr array for port (commport-1).
  998.  *
  999.  *      int  iThisDev - index into vcr array for this device.
  1000.  *                          (daisy_chain_position - 1)
  1001.  *
  1002.  *      LPWSTR lpszNewName - return buffer with the Good Name!
  1003.  *
  1004.  * Returns: TRUE if successful.
  1005.  *
  1006.  ***************************************************************************/
  1007. static BOOL NEAR PASCAL
  1008. MakeMeAGoodName(HWND hDlg, int iPort, int iThisDev, LPWSTR lpszNewName)
  1009. {
  1010.     int     iDev         = 0;
  1011.     int     iAGoodNumber = 0;  // 0 should map to --> none.
  1012.     int     iTempPort    = 0;
  1013.     WCHAR   szAGoodTry[ONEENTRY_LENGTH];
  1014.  
  1015.     //
  1016.     // Read all the names into the array now! (Update any outstanding).
  1017.     //
  1018.     viscaDlgRead(hDlg, iPort);
  1019.  
  1020.     while(1)
  1021.     {
  1022.         if(iAGoodNumber==0)
  1023.             lstrcpy(szAGoodTry, szKeyName);
  1024.         else
  1025.             wsprintf(szAGoodTry, TEXT("%s%d"), (LPSTR)szKeyName, iAGoodNumber);
  1026.  
  1027.         DPF(DBG_CONFIG, "MakeMeAGoodName - Trying=%s\n", (LPWSTR)szAGoodTry);
  1028.  
  1029.         for(iTempPort = 0; iTempPort < MAXPORTS; iTempPort++)
  1030.         {
  1031.             for(iDev = 0; iDev < MAXDEVICES; iDev++)
  1032.             {
  1033.                 if(lstrcmpi(szAGoodTry, pvcr->Port[iTempPort].Dev[iDev].szVcrName) == 0)
  1034.                     goto StartOver;
  1035.             }
  1036.         }
  1037.         break; // Success, it was not found anywhere in the table!
  1038.  
  1039.         StartOver:
  1040.         iAGoodNumber++;
  1041.     }
  1042.     //
  1043.     // We will eventually find a good name and end up here.!
  1044.     //
  1045.     lstrcpy(lpszNewName, szAGoodTry); //Do it up!
  1046.     // Nothing prevents a person from themselves duplicating a name?
  1047.     return TRUE;
  1048. }
  1049.  
  1050.  
  1051. /****************************************************************************
  1052.  * Function: BOOL viscaDlgUpdateNumDevs - Called when number of vcrs in configuration
  1053.  *          dialog is changed.
  1054.  *
  1055.  * Parameters:
  1056.  *
  1057.  *      HWND hDlg - Configuration dialog window.
  1058.  *
  1059.  *      int  iPort - index into vcr array corresponding to commport (commport-1).
  1060.  *
  1061.  *      int  iNumDevices - Number of vcrs selected.
  1062.  *
  1063.  * Returns: TRUE if successful.
  1064.  *
  1065.  ***************************************************************************/
  1066. static BOOL NEAR PASCAL
  1067. viscaDlgUpdateNumDevs(HWND hDlg, int iPort, int iNumDevices)  // Sends new commport.
  1068. {
  1069.     int iDev;
  1070.  
  1071.     DPF(DBG_CONFIG, "viscaDlgUpdateNumDevs Port=%d\n", iPort);
  1072. #ifdef DEBUG
  1073.     //
  1074.     // First set all entries to BLANKs (Clearing whats there).
  1075.     //
  1076.     for(iDev=0; iDev < MAXDEVICES; iDev++)
  1077.     {
  1078.         EnableWindow(GetDlgItem(hDlg, IDD_VCRONE + iDev), TRUE);
  1079.         SetDlgItemText(hDlg, IDD_VCRONE + iDev, (LPWSTR)szNull);
  1080.     }
  1081. #endif
  1082.     //
  1083.     // Blank out the end of the list.
  1084.     //
  1085.     for(iDev = iNumDevices; iDev < MAXDEVICES; iDev++)
  1086.         pvcr->Port[iPort].Dev[iDev].szVcrName[0] = TEXT('\0');
  1087.  
  1088.     //
  1089.     // If user left holes then fill them with made up names.
  1090.     //
  1091.     if(iNumDevices != 0)
  1092.     {
  1093.         for(iDev=0; iDev < iNumDevices; iDev++)
  1094.         {
  1095.             if(pvcr->Port[iPort].Dev[iDev].szVcrName[0] == TEXT('\0'))
  1096.             {
  1097.                 // Make a good name here! 
  1098.                 MakeMeAGoodName(hDlg, iPort, iDev, (LPWSTR)pvcr->Port[iPort].Dev[iDev].szVcrName);
  1099.                 DPF(DBG_CONFIG, "viscaDlgUpdateNumDevsChange - making a name at Port=%d Dev=%d\n", iPort, iDev);
  1100.             }
  1101.  
  1102.             DPF(DBG_CONFIG, "viscaDlgUpdateNumDevs Port=%d Dev=%d string=%s", iPort, iDev, (LPWSTR)pvcr->Port[iPort].Dev[iDev].szVcrName);
  1103.  
  1104. #ifdef DEBUG
  1105.             //
  1106.             // Add the names to the list box.
  1107.             //
  1108.             EnableWindow(GetDlgItem(hDlg, IDD_VCRONE + iDev), TRUE);
  1109.             SetDlgItemText(hDlg, IDD_VCRONE + iDev, (LPWSTR)pvcr->Port[iPort].Dev[iDev].szVcrName);
  1110. #endif
  1111.         }
  1112.     }
  1113.     //
  1114.     // Disable all remaining edit boxes!
  1115.     //
  1116. #ifdef DEBUG
  1117.     for(;iDev < MAXDEVICES; iDev++)
  1118.     {
  1119.        EnableWindow(GetDlgItem(hDlg, IDD_VCRONE + iDev), FALSE);
  1120.     }
  1121. #endif
  1122.  
  1123.     return TRUE;
  1124. }
  1125.  
  1126.  
  1127. /****************************************************************************
  1128.  * Function: BOOL viscaDlgRead - Read the configuration dialog strings into the vcr array.
  1129.  *
  1130.  * Parameters:
  1131.  *
  1132.  *      HWND hDlg - Configuration dialog window.
  1133.  *
  1134.  *      int  iPort - index into vcr array corresponding to commport (commport-1).
  1135.  *
  1136.  * Returns: TRUE if successful.
  1137.  *
  1138.  *       This is done if 1. commport changed, or 2. Ok is pressed.
  1139.  *
  1140.  ***************************************************************************/
  1141. static BOOL NEAR PASCAL
  1142. viscaDlgRead(HWND hDlg, int iPort)  // Port config to read things into.
  1143. {
  1144.     int iNumDevs = ComboBox_GetCurSel(GetDlgItem(hDlg, IDD_COMBO_DEVICE)); // 0 relative
  1145. #ifdef DEBUG
  1146.     WCHAR szTempVcrName[VCRNAME_LENGTH];
  1147.     WCHAR szFailure[MESSAGE_LENGTH];
  1148.     WCHAR szTitle[TITLE_LENGTH];
  1149.     WCHAR szMessage[MESSAGE_LENGTH];
  1150.  
  1151.     int iDev, i, j;
  1152.     
  1153.     // Read ALL, so we reset contents of potentially blanked ones.
  1154.     for(iDev = 0; iDev < MAXDEVICES; iDev++)
  1155.     {
  1156.         GetDlgItemText(hDlg, IDD_VCRONE+iDev, (LPWSTR)szTempVcrName, VCRNAME_LENGTH - 1);
  1157.     
  1158.         i = 0;
  1159.  
  1160.         while((szTempVcrName[i] != TEXT('\0')) && !IsAlphaNumeric(szTempVcrName[i]))
  1161.             i++;
  1162.  
  1163.         if(IsDigit(szTempVcrName[i]))
  1164.         {
  1165.             if(LoadString(hModuleInstance, IDS_CONFIG_ERR, szMessage, sizeof(szMessage) / sizeof(WCHAR)))
  1166.                 if(LoadString(hModuleInstance, IDS_CONFIG_ERR_ILLEGALNAME, szTitle, sizeof(szTitle) / sizeof(WCHAR)))
  1167.                 {
  1168.                     wsprintf((LPWSTR)szFailure, (LPWSTR)szMessage, (LPWSTR)szTempVcrName);
  1169.                     MessageBox(hDlg, (LPWSTR)szFailure, (LPWSTR)szTitle, MB_OK);
  1170.                     return FALSE;
  1171.                 }
  1172.  
  1173.         }
  1174.  
  1175.         j = 0;
  1176.         while((szTempVcrName[i] != TEXT('\0')) && IsAlphaNumeric(szTempVcrName[i]))
  1177.         {
  1178.             pvcr->Port[iPort].Dev[iDev].szVcrName[j] = szTempVcrName[i];
  1179.             j++;
  1180.             i++;
  1181.         }
  1182.         pvcr->Port[iPort].Dev[iDev].szVcrName[j] = TEXT('\0');
  1183.     }
  1184. #endif
  1185.     return TRUE;
  1186. }
  1187.  
  1188.  
  1189. /****************************************************************************
  1190.  * Function: BOOL viscaCheckTotalEntries - Make sure there is at least one entry!
  1191.  *          0 is bad because then all mcivisca.drv will be removed from
  1192.  *          system.ini which means it won't appear in the drivers configuration
  1193.  *          list box.
  1194.  *
  1195.  * Parameters:
  1196.  *
  1197.  *      HWND hDlg - Configuration dialog window.
  1198.  *
  1199.  * Returns: TRUE if there is more than 0 enties.
  1200.  *
  1201.  ***************************************************************************/
  1202. static BOOL NEAR PASCAL
  1203. viscaCheckTotalEntries(HWND hDlg)
  1204. {
  1205.     int iPort, iDev;
  1206.     int iHitCount = 0;
  1207.     WCHAR szTitle[TITLE_LENGTH];
  1208.     WCHAR szMessage[MESSAGE_LENGTH];
  1209.  
  1210.     for(iPort = 0; iPort < MAXPORTS; iPort++)
  1211.     {
  1212.         for(iDev = 0; iDev < MAXDEVICES; iDev++)
  1213.         {
  1214.             if(pvcr->Port[iPort].Dev[iDev].szVcrName[0] != TEXT('\0'))
  1215.                 iHitCount++;
  1216.         }
  1217.     }
  1218.  
  1219.     if(iHitCount > 0)
  1220.         return TRUE;
  1221.  
  1222.     DPF(DBG_CONFIG, "viscaCheckTotalEntries HitCount==0");
  1223.  
  1224.     if(LoadString(hModuleInstance, IDS_CONFIG_WARN_LASTVCR, szMessage, sizeof(szMessage) / sizeof(WCHAR)))
  1225.     {
  1226.         if(LoadString(hModuleInstance, IDS_CONFIG_WARN, szTitle, sizeof(szTitle) / sizeof(WCHAR)))
  1227.         {
  1228.             if(MessageBox(hDlg, (LPWSTR)szMessage, (LPWSTR)szTitle, MB_YESNO) == IDYES)
  1229.                 return TRUE;
  1230.             else
  1231.                 return FALSE;
  1232.         }
  1233.     }
  1234. }
  1235.  
  1236.  
  1237. /****************************************************************************
  1238.  * Function: BOOL viscaIsCommPort - Determines if the commport hardware exists.
  1239.  *
  1240.  * Parameters:
  1241.  *
  1242.  *      LPSTR lpstrCommPort  - String describing the commport.
  1243.  *
  1244.  * Returns: TRUE if the commport hardware exists.
  1245.  *
  1246.  ***************************************************************************/
  1247. static BOOL NEAR PASCAL
  1248. viscaIsCommPort(LPWSTR lpstrCommPort)
  1249. {
  1250.     VISCACOMMHANDLE iCommId;
  1251. #ifdef WIN32
  1252.     iCommId = CreateFile(lpstrCommPort, GENERIC_READ | GENERIC_WRITE,
  1253.                             0,              // exclusive access
  1254.                             NULL,           // no security
  1255.                             OPEN_EXISTING,
  1256.                             FILE_ATTRIBUTE_NORMAL, // | FILE_FLAG_OVERLAPPED
  1257.                             NULL);
  1258.  
  1259.     if(iCommId==INVALID_HANDLE_VALUE)
  1260.         return FALSE;
  1261.  
  1262.     if(iCommId != NULL)
  1263.         CloseHandle(iCommId);
  1264.     return TRUE;
  1265. #else
  1266.     if((iCommId = OpenComm(lpstrCommPort, 1, 1)) == IE_HARDWARE)
  1267.     {
  1268.         return FALSE;
  1269.     }
  1270.     else if(iCommId < 0)
  1271.     {
  1272.         return TRUE; // Okay maybe it will open later.
  1273.     }
  1274.     else
  1275.     {
  1276.         // Good commport Close and return true.
  1277.         CloseComm(iCommId);
  1278.         return TRUE;
  1279.     }
  1280. #endif
  1281. }
  1282.  
  1283.  
  1284. /****************************************************************************
  1285.  * Function: BOOL viscaConfigDlgInit - Perform initialization of configuration
  1286.  *              dialog box in response to the WM_INITDIALOG message.
  1287.  *
  1288.  * Parameters:
  1289.  *
  1290.  *      HWND hDlg - Handle to dialog window.
  1291.  *
  1292.  *      HWND hwndFocus - Handle to first control that can be given focus.
  1293.  *
  1294.  *      LPARAM lParam - Pointer to driver configuration
  1295.  *                         sturcture.
  1296.  *
  1297.  * Returns: TRUE if successful, otherwise FALSE.
  1298.  ***************************************************************************/
  1299. static BOOL NEAR PASCAL
  1300. viscaConfigDlgInit(HWND hDlg, HWND hwndFocus, LPARAM lParam)
  1301. {
  1302.     UINT    nPort;
  1303.     UINT    nDevice;
  1304.     HWND    hComboPort;
  1305.     HWND    hComboDevice;
  1306.     WCHAR   szText[PORT_LENGTH];
  1307.     int     iPort, iDev;
  1308.     int     i;
  1309.  
  1310.     // fill in port combo box
  1311.     hComboPort = GetDlgItem(hDlg, IDD_COMBO_PORT);
  1312.     if (hComboPort == NULL) {
  1313.         return (FALSE);
  1314.     }
  1315.     ComboBox_ResetContent(hComboPort);
  1316.     for (nPort = 0; nPort < MAXPORTS; nPort++)
  1317.     {
  1318.         LoadString(hModuleInstance, IDS_COM1 + nPort, szText, sizeof(szText));
  1319.         pvcr->Port[nPort].fExists = FALSE;
  1320.  
  1321.         if(pvcr->Port[nPort].nUsage > 0)   // Open and in use right now by a vcr.
  1322.              pvcr->Port[nPort].fExists = TRUE;
  1323.         else if(viscaIsCommPort(szText))
  1324.              pvcr->Port[nPort].fExists = TRUE;
  1325.  
  1326.         if(pvcr->Port[nPort].fExists)
  1327.             ComboBox_AddString(hComboPort, szText);
  1328.     }
  1329.  
  1330.     // fill in device combo box
  1331.     hComboDevice = GetDlgItem(hDlg, IDD_COMBO_DEVICE);
  1332.     if (hComboDevice == NULL) 
  1333.         return (FALSE);
  1334.  
  1335.     // This combo box is now the number of devices, not the device.
  1336.    
  1337.     ComboBox_ResetContent(hComboDevice);
  1338.     for (nDevice = 0; nDevice <= MAXDEVICES; nDevice++)
  1339.     {
  1340.         wsprintf(szText, TEXT("%d"), nDevice);
  1341.         ComboBox_AddString(hComboDevice, szText);
  1342.     }
  1343.     lstrcpy(szKeyName, ((LPDRVCONFIGINFO)lParam)->lpszDCIAliasName);
  1344.     for(i=0; i < lstrlen(szKeyName); i++)
  1345.         if(!IsAlpha(szKeyName[i]))
  1346.             szKeyName[i] = TEXT('\0');  // If vcr1 comes in, cut key to vcr
  1347.                                         // otherwise when we make names it will all be vcr11, etc.
  1348.     //
  1349.     // Initialize the VCR table if we are going to read (not on delete).
  1350.     //
  1351.     for (iPort = 0; iPort < MAXPORTS; iPort++)
  1352.     {
  1353.         for(iDev = 0; iDev < MAXDEVICES; iDev++)
  1354.             pvcr->Port[iPort].Dev[iDev].szVcrName[0] = TEXT('\0');
  1355.     }
  1356.     //
  1357.     // Read the entry names into the Vcr array.
  1358.     //
  1359.     viscaAllVcrs(((LPDRVCONFIGINFO)lParam)->lpszDCISectionName, FALSE); // Do not delete; read!
  1360.     //
  1361.     // Some user might have deleted the first entry so check for all.
  1362.     //
  1363.     for(nPort = 0; nPort < MAXPORTS; nPort++)
  1364.     {
  1365.         if(!pvcr->Port[nPort].fExists)
  1366.         {
  1367.             for(nDevice = 0; nDevice < MAXDEVICES; nDevice++)
  1368.                 pvcr->Port[nPort].Dev[nDevice].szVcrName[0] = TEXT('\0');
  1369.         }
  1370.         else
  1371.         {
  1372.             for(nDevice = 0; nDevice < MAXDEVICES; nDevice++)
  1373.             {
  1374.                 if(pvcr->Port[nPort].Dev[nDevice].szVcrName[0] != TEXT('\0'))
  1375.                     goto OutOfLoops;
  1376.             }
  1377.         }
  1378.     }
  1379.             
  1380.     OutOfLoops:
  1381.  
  1382.     if(nPort == MAXPORTS)  // If Max, start with first port that exists.
  1383.     {
  1384.         for(nPort=0; !pvcr->Port[nPort].fExists && (nPort < MAXPORTS); nPort++);
  1385.     }
  1386.  
  1387.     if(nPort == MAXPORTS)   // No serial ports exist!!! Error.
  1388.         return FALSE;
  1389.     //
  1390.     // Show the current port and device selection.
  1391.     //
  1392.     viscaDlgUpdatePort(hDlg, nPort);
  1393.     
  1394.     return (TRUE);
  1395. }
  1396.  
  1397.  
  1398. /****************************************************************************
  1399.  * Function: BOOL viscaIsDoubleOnPort - Determines if any of the names appearing
  1400.  *          in the 1..7 part of the configuration dialog box is repeated anywhere
  1401.  *          in the entire vcr array.
  1402.  *
  1403.  * Parameters:
  1404.  *
  1405.  *      HWND hDlg - Configuration dialog window.
  1406.  *
  1407.  *      int  iPort - index into vcr array corresponding to commport (commport-1).
  1408.  *
  1409.  * Returns: TRUE if there is at least one repeated name.
  1410.  *
  1411.  ***************************************************************************/
  1412. static BOOL NEAR PASCAL
  1413. viscaIsDoubleOnPort(HWND hDlg, int iPort)
  1414. {
  1415. #ifdef DEBUG
  1416.     WCHAR   szTitle[TITLE_LENGTH];
  1417.     WCHAR   szMessage[MESSAGE_LENGTH];
  1418.     WCHAR   szCheck[VCRNAME_LENGTH];
  1419.     WCHAR   szDoubleFailure[MESSAGE_LENGTH];
  1420.     int     iDev;
  1421.     int     iOtherPort, iOtherDev;
  1422.     int     iNumHits;
  1423.     int     iNumDevs;
  1424.     //
  1425.     // Read any outstanding text now. (if bad names, fail here).
  1426.     //
  1427. #else
  1428.     if(!viscaDlgRead(hDlg, iPort))
  1429.         return TRUE; // True means something bad happened.
  1430. #endif
  1431.  
  1432. #ifdef DEBUG
  1433.  
  1434.     iNumDevs = ComboBox_GetCurSel(GetDlgItem(hDlg, IDD_COMBO_DEVICE));
  1435.     //
  1436.     // Loop through all devices listed on this port.
  1437.     //
  1438.     for(iDev=0; iDev < iNumDevs; iDev++)
  1439.     {
  1440.         GetDlgItemText(hDlg, IDD_VCRONE+iDev, (LPWSTR) szCheck, VCRNAME_LENGTH - 1);
  1441.      
  1442.         iNumHits = 0;
  1443.         //
  1444.         // Loop through all devices on all ports.
  1445.         //
  1446.         for(iOtherPort=0; iOtherPort < MAXPORTS; iOtherPort++)
  1447.             for(iOtherDev=0; iOtherDev < MAXDEVICES; iOtherDev++)
  1448.                 if(lstrcmpi((LPWSTR)szCheck, (LPWSTR) pvcr->Port[iOtherPort].Dev[iOtherDev].szVcrName)==0)
  1449.                     iNumHits++;
  1450.  
  1451.         if(iNumHits > 1)
  1452.         {
  1453.             DPF(DBG_CONFIG, "viscaIsDoubleOnPort - szDoubleName=%s", (LPWSTR)szCheck);
  1454.  
  1455.             if(LoadString(hModuleInstance, IDS_CONFIG_ERR_REPEATNAME, szMessage, sizeof(szMessage) / sizeof(WCHAR)))
  1456.             {
  1457.                 if(LoadString(hModuleInstance, IDS_CONFIG_ERR, szTitle, sizeof(szTitle) / sizeof(WCHAR)))
  1458.                 {
  1459.                     wsprintf((LPWSTR)szDoubleFailure, (LPWSTR)szMessage, (LPWSTR)szCheck);
  1460.                     MessageBox(hDlg, (LPWSTR)szDoubleFailure, (LPWSTR)szTitle, MB_OK);
  1461.                     return TRUE;
  1462.                 }
  1463.             }
  1464.         }
  1465.     }
  1466. #endif
  1467.     return FALSE; //No doubles on this port.
  1468. }
  1469.  
  1470.  
  1471. /****************************************************************************
  1472.  * Function: int viscaComboGetPort - Get the selected port from the combo box.
  1473.  *
  1474.  * Parameters:
  1475.  *
  1476.  *      HWND hDlg - Configuration dialog window.
  1477.  *
  1478.  * Returns: index into vcr array (commport - 1) if successful, otherwise < 0.
  1479.  *
  1480.  ***************************************************************************/
  1481. static int NEAR PASCAL
  1482. viscaComboGetPort(HWND hDlg)
  1483. {
  1484.     int     iIndexCombo = ComboBox_GetCurSel(GetDlgItem(hDlg, IDD_COMBO_PORT));
  1485.     int     iExistingCount, i;
  1486.  
  1487.     DPF(DBG_CONFIG, "viscaComboGetPort - called.\n");
  1488.  
  1489.     DPF(DBG_CONFIG, "viscaComboGetPort - iIndexCombo = %d \n", iIndexCombo);
  1490.  
  1491.     // 
  1492.     // Find the nth (indexcombo) existing port.
  1493.     //
  1494.     
  1495.     for(iExistingCount=0, i=0; (iExistingCount <= iIndexCombo) && (i < MAXPORTS); i++)
  1496.     {
  1497.         if(pvcr->Port[i].fExists)
  1498.             iExistingCount++;
  1499.     }
  1500.  
  1501.     if(iExistingCount <= iIndexCombo) //We ran out of ports (can't check max, because it can come true at same time).
  1502.         i = 1; //Just set to the first possible commport (not possible).
  1503.  
  1504.     DPF(DBG_CONFIG, "viscaComboGetPort - iPort = %d\n", i - 1);
  1505.  
  1506.     return i - 1;
  1507.  
  1508. }
  1509.  
  1510.  
  1511. /****************************************************************************
  1512.  * Function: void viscaConfigDlgCommand - Process the WM_COMMAND message for
  1513.  *              the configuration dialog box.
  1514.  *
  1515.  * Parameters:
  1516.  *
  1517.  *      HWND hDlg - Handle to dialog window.
  1518.  *
  1519.  *      int id - Identifier of control.
  1520.  *
  1521.  *      HWND hwndCtl - Handle to control sending the message.
  1522.  *
  1523.  *      UINT codeNotify - Notification message.
  1524.  *
  1525.  *      
  1526.  *  If commport changed,
  1527.  *      1. send last commport to read
  1528.  *      2. send commport change to updatecommport.
  1529.  *
  1530.  *  If number of devices changed
  1531.  *      1. send commport to updatenumberdevices.
  1532.  *
  1533.  ***************************************************************************/
  1534. static void NEAR PASCAL
  1535. viscaConfigDlgCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
  1536. {
  1537.     switch(id)
  1538.     {
  1539.         case IDDETECT:
  1540.         {
  1541.             int iPort    = viscaComboGetPort(hDlg);
  1542.             int iNumDevs = 0;
  1543.  
  1544.             if(pvcr->Port[iPort].nUsage > 0)
  1545.                 iNumDevs     = pvcr->Port[iPort].nDevices;
  1546.             else
  1547.                 iNumDevs     = viscaDetectOnCommPort(iPort);
  1548.  
  1549.             // On error set the number to 0.
  1550.             if(iNumDevs < 0) 
  1551.                 iNumDevs = 0;
  1552.  
  1553.             ComboBox_SetCurSel(GetDlgItem(hDlg, IDD_COMBO_DEVICE), iNumDevs); // 0==>0, 1==>1, etc.
  1554.             viscaDlgUpdateNumDevs(hDlg, iPort, iNumDevs);
  1555.             pvcr->iLastNumDevs = iNumDevs;
  1556.         }
  1557.         break;
  1558.  
  1559.         case IDOK:
  1560.         {
  1561.             //
  1562.             // IsDouble will read all the entries, so don't worry.
  1563.             //
  1564.             if(!viscaIsDoubleOnPort(hDlg, pvcr->iLastPort))
  1565.             {
  1566.                 if(viscaCheckTotalEntries(hDlg)) //Check if there is at least one.
  1567.                     EndDialog(hDlg, TRUE);
  1568.             }
  1569.         }
  1570.             break;
  1571.  
  1572.         case IDCANCEL:
  1573.             EndDialog(hDlg, 0);
  1574.             break;
  1575.  
  1576.         case IDD_COMBO_DEVICE:
  1577.             if(codeNotify == CBN_SELCHANGE)
  1578.             {
  1579.                 int iNumDevs = ComboBox_GetCurSel(GetDlgItem(hDlg, IDD_COMBO_DEVICE));
  1580.                 int iPort    = viscaComboGetPort(hDlg); //Array index
  1581.  
  1582.                 if(iNumDevs != pvcr->iLastNumDevs)
  1583.                 {
  1584.                     // No nead to read the dlg now!
  1585.                     viscaDlgUpdateNumDevs(hDlg, iPort, iNumDevs);
  1586.                     pvcr->iLastNumDevs = iNumDevs;
  1587.  
  1588.                 }
  1589.  
  1590.             }
  1591.             break;
  1592.  
  1593.         case IDD_COMBO_PORT:
  1594.             if(codeNotify == CBN_SELCHANGE)
  1595.             {
  1596.                 int iPort = viscaComboGetPort(hDlg); //Array index.
  1597.                 //
  1598.                 // viscaIsDoubleOnPort will read all the entries, so don't worry.
  1599.                 //
  1600.                 if(viscaIsDoubleOnPort(hDlg, pvcr->iLastPort))
  1601.                 {
  1602.                     viscaDlgUpdatePort(hDlg, pvcr->iLastPort);  // Will set port back correctly.
  1603.                 }
  1604.                 else
  1605.                 {
  1606.                     if(iPort != pvcr->iLastPort)
  1607.                     {
  1608.                         viscaDlgUpdatePort(hDlg, iPort);
  1609.                         pvcr->iLastPort = iPort;
  1610.                     }
  1611.                 }
  1612.             }
  1613.             break;
  1614.     }
  1615. }
  1616.  
  1617.  
  1618. /****************************************************************************
  1619.  * Function: BOOL viscaConfigDlgProc - Dialog function for configuration dialog.
  1620.  *
  1621.  * Parameters:
  1622.  *
  1623.  *      HWND hDlg - Handle to dialog window.
  1624.  *
  1625.  *      UINT uMsg - Windows message.
  1626.  *
  1627.  *      WPARAM wParam - First message-specific parameter.
  1628.  *
  1629.  *      LPARAM lParam - Second message-specific parameter.
  1630.  *
  1631.  * Returns: TRUE if message was processed, otherwise FALSE.
  1632.  ***************************************************************************/
  1633. BOOL CALLBACK LOADDS
  1634. viscaConfigDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1635. {
  1636.     switch (uMsg) {
  1637.         case WM_INITDIALOG:
  1638.             return (BOOL)HANDLE_WM_INITDIALOG(hDlg, wParam, lParam, 
  1639.                                               viscaConfigDlgInit);
  1640.  
  1641.         case WM_COMMAND:
  1642.             HANDLE_WM_COMMAND(hDlg, wParam, lParam, viscaConfigDlgCommand);
  1643.             return (FALSE);
  1644.     }
  1645.  
  1646.     return (FALSE);
  1647. }
  1648.  
  1649.  
  1650. /****************************************************************************
  1651.  * Function: LRESULT viscaConfig - User configuration.
  1652.  *
  1653.  * Parameters:
  1654.  *
  1655.  *      HWND hwndParent - Window to use as parent of configuration dialog.
  1656.  *
  1657.  *      LPDRVCONFIGINFO lpConfig - Config data.
  1658.  *
  1659.  *      HINSTANCE hInstance - Instance handle of module.
  1660.  *
  1661.  * Returns: the result of DialogBoxParam().
  1662.  ***************************************************************************/
  1663. static LRESULT NEAR PASCAL
  1664. viscaConfig(HWND hwndParent, LPDRVCONFIGINFO lpConfig, HINSTANCE hInstance)
  1665. {
  1666.     int iResult = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_VISCACNFG),
  1667.                 hwndParent, (DLGPROC)viscaConfigDlgProc, (DWORD)lpConfig);
  1668.  
  1669.     if(iResult)
  1670.     {
  1671.         viscaAllVcrs(lpConfig->lpszDCISectionName, TRUE); // Delete.
  1672.         viscaWriteAllVcrs(lpConfig->lpszDCISectionName);
  1673.     }
  1674.  
  1675.     return (DRV_OK);
  1676. }
  1677.  
  1678.  
  1679. /****************************************************************************
  1680.  * Function: LRESULT viscaRemove - Respond to DRV_REMOVE message.
  1681.  *
  1682.  * Parameters:
  1683.  *
  1684.  *      HDRVR hDriver - Handle to driver being removed.
  1685.  *
  1686.  * Returns: TRUE on success, otherwise FALSE.
  1687.  ***************************************************************************/
  1688. static LRESULT NEAR PASCAL
  1689. viscaRemove(HDRVR hDriver)
  1690. {
  1691.     return ((LRESULT)TRUE);
  1692. }
  1693.  
  1694.  
  1695. /****************************************************************************
  1696.  * Function: int viscaDetectOnCommPort - Detect the number of VCRs on this commport.
  1697.  *
  1698.  * Parameters:
  1699.  *
  1700.  *      int  iPort - index into vcr array of port. (commport - 1).
  1701.  *
  1702.  * Returns: Number of VCRs (can be 0) or -1 for error.
  1703.  *
  1704.  ***************************************************************************/
  1705. static int NEAR PASCAL
  1706. viscaDetectOnCommPort(int iPort)
  1707. {
  1708.     BYTE    achPacket[MAXPACKETLENGTH];
  1709.     DWORD   dwErr;
  1710.     int     iInst;
  1711.     int     iDev = 0; //We will call ourselves the first device on the serial port.
  1712.  
  1713.     pvcr->fConfigure = TRUE;    // This also acks to synchronize
  1714.     
  1715.     iInst = viscaInstanceCreate(0, iPort, iDev); //0 means don't use MCI
  1716.     if (iInst == -1) 
  1717.         return -1;
  1718.  
  1719.     if (!viscaTaskIsRunning())
  1720.     {
  1721.         if (!viscaTaskCreate())
  1722.         {
  1723.             DPF(DBG_ERROR, "Failed to create task.\n");
  1724.             viscaInstanceDestroy(iInst);
  1725.             return -1;
  1726.         }
  1727.     }
  1728.     //
  1729.     // Global handles are created immediately when the task starts up.
  1730.     //
  1731.     DuplicateGlobalHandlesToInstance(pvcr->htaskCommNotifyHandler, iInst);  // Always do this immediately.
  1732.     //
  1733.     // Okay, open the port. 
  1734.     //
  1735.     viscaTaskDo(iInst, TASKOPENCOMM, iPort + 1, 0);
  1736.     if(pvcr->Port[iPort].idComDev < 0)
  1737.     {
  1738.         viscaInstanceDestroy(iInst);
  1739.         return -1;
  1740.     }
  1741.     DuplicatePortHandlesToInstance(pvcr->htaskCommNotifyHandler, iPort, iInst);
  1742.     //
  1743.     // Open the device.
  1744.     //
  1745.     viscaTaskDo(iInst, TASKOPENDEVICE, iPort, iDev);
  1746.     DuplicateDeviceHandlesToInstance(pvcr->htaskCommNotifyHandler, iPort, iDev, iInst);
  1747.     //
  1748.     // We have the green light to begin sending commands.
  1749.     //
  1750.     pvcr->Port[iPort].Dev[iDev].fDeviceOk = TRUE;
  1751.     //
  1752.     // There is no completion on non-broadcasted! (so who releases it?)
  1753.     //
  1754.     dwErr = viscaDoImmediateCommand(iInst, BROADCASTADDRESS,
  1755.                         achPacket,
  1756.                         viscaMessageIF_Address(achPacket + 1));
  1757.     if (dwErr)
  1758.     {
  1759.         viscaTaskDo(iInst, TASKCLOSECOMM, iPort + 1, 0); //Porthandles destroyed.
  1760.         viscaTaskDo(iInst, TASKCLOSEDEVICE, iPort, iDev); //Porthandles destroyed.
  1761.         viscaInstanceDestroy(iInst);
  1762.         return 0; //No devices.
  1763.     }
  1764.  
  1765.     viscaTaskDo(iInst, TASKCLOSECOMM, iPort + 1, 0); //Porthandles destroyed.
  1766.     viscaTaskDo(iInst, TASKCLOSEDEVICE, iPort, iDev); //Porthandles destroyed.
  1767.  
  1768.     viscaInstanceDestroy(iInst);
  1769.  
  1770.     pvcr->fConfigure = FALSE;
  1771.  
  1772.     DPF(DBG_CONFIG, "viscaDetectOnCommPort --> detect %d", (int)(BYTE)achPacket[2] - 1);
  1773.  
  1774.     return (int)(BYTE)achPacket[2] - 1; // -1 for the computer.
  1775. }
  1776.  
  1777.  
  1778. extern LRESULT FAR PASCAL DefDriverProc(
  1779.     DWORD   dDriverID,
  1780.     HANDLE  hDriver,
  1781.     UINT    wMessage,
  1782.     LONG    lParam1,
  1783.     LONG    lParam2);
  1784.  
  1785.  
  1786. /***************************************************************************
  1787.  * Function: LONG DriverProc - Windows driver entry point.  All Windows driver
  1788.  *     control messages and all MCI messages pass through this entry point.
  1789.  *                
  1790.  * Parameters:
  1791.  *
  1792.  *      DWORD dwDriverId - For most messages, <p dwDriverId> is the DWORD
  1793.  *     value that the driver returns in response to a <m DRV_OPEN> message.
  1794.  *     Each time that the driver is opened, through the <f DrvOpen> API,
  1795.  *     the driver receives a <m DRV_OPEN> message and can return an
  1796.  *     arbitrary, non-zero value. The installable driver interface
  1797.  *     saves this value and returns a unique driver handle to the 
  1798.  *     application. Whenever the application sends a message to the
  1799.  *     driver using the driver handle, the interface routes the message
  1800.  *     to this entry point and passes the corresponding <p dwDriverId>.
  1801.  *     This mechanism allows the driver to use the same or different
  1802.  *     identifiers for multiple opens but ensures that driver handles
  1803.  *     are unique at the application interface layer.
  1804.  *
  1805.  *     The following messages are not related to a particular open
  1806.  *     instance of the driver. For these messages, the dwDriverId
  1807.  *     will always be zero.
  1808.  *
  1809.  *         DRV_LOAD, DRV_FREE, DRV_ENABLE, DRV_DISABLE, DRV_OPEN
  1810.  *
  1811.  *      HDRVR  hDriver - This is the handle returned to the
  1812.  *     application by the driver interface.
  1813.  *          
  1814.  *      UINT uMessage - The requested action to be performed. Message
  1815.  *     values below <m DRV_RESERVED> are used for globally defined messages.
  1816.  *     Message values from <m DRV_RESERVED> to <m DRV_USER> are used for
  1817.  *     defined driver protocols.  Messages above <m DRV_USER> are used
  1818.  *     for driver specific messages.
  1819.  *
  1820.  *      LPARAM lParam1 - Data for this message.  Defined separately for
  1821.  *     each message
  1822.  *
  1823.  *      LPARAM lParam2 - Data for this message.  Defined separately for
  1824.  *     each message
  1825.  *
  1826.  * Returns: Defined separately for each message. 
  1827.  ***************************************************************************/
  1828. LRESULT CALLBACK LOADDS
  1829. DriverProc(DWORD dwDriverID, HDRVR hDriver, UINT uMessage, LPARAM lParam1, LPARAM lParam2)
  1830. {
  1831.     switch (uMessage) {
  1832.  
  1833.         case DRV_LOAD:
  1834.             /* the DRV_LOAD message is received once, when the driver is */
  1835.             /* first loaded - any one-time initialization code goes here */
  1836.             return (viscaDrvLoad());
  1837.  
  1838.         case DRV_FREE:
  1839.             /* the DRV_FREE message is received once when the driver is */
  1840.             /* unloaded - any final shut down code goes here */
  1841.             return (viscaDrvFree(LOWORD(dwDriverID)));
  1842.  
  1843.         case DRV_OPEN:
  1844.             /* the DRV_OPEN message is received once for each MCI device open */
  1845.             if (lParam2) {                  // normal open
  1846.                 return (viscaDrvOpen((LPWSTR)lParam1,
  1847.                         (LPMCI_OPEN_DRIVER_PARMS)lParam2));
  1848.             }
  1849.             else {                                  // configuration open
  1850.                 return (0x00010000);
  1851.             }
  1852.  
  1853.         case DRV_CLOSE:
  1854.             /* this message is received once for each MCI device close */
  1855.             return (viscaDrvClose(LOWORD(dwDriverID)));
  1856.  
  1857.         case DRV_QUERYCONFIGURE:
  1858.             /* the DRV_QUERYCONFIGURE message is used to determine if the */
  1859.             /* DRV_CONCIGURE message is supported - return 1 to indicate */
  1860.             /* configuration is supported */
  1861.             return (1L);
  1862.  
  1863.         case DRV_CONFIGURE:
  1864.             /* the DRV_CONFIGURE message instructs the device to perform */
  1865.             /* device configuration. */
  1866.             if (lParam2 && lParam1 &&
  1867.                 (((LPDRVCONFIGINFO)lParam2)->dwDCISize == sizeof(DRVCONFIGINFO)))
  1868.             {
  1869.                 return (viscaConfig((HWND)WINWORD(lParam1), (LPDRVCONFIGINFO)lParam2, hModuleInstance));
  1870.             }
  1871.             break;
  1872.         
  1873.         case DRV_REMOVE:
  1874.             /* the DRV_REMOVE message informs the driver that it is being removed */
  1875.             /* from the system */
  1876.             return (viscaRemove(hDriver));
  1877.  
  1878.         default:
  1879.             /* all other messages are processed here */
  1880.  
  1881.             /* select messages in the MCI range */
  1882.             if ((!HIWORD(dwDriverID)) && (uMessage >= DRV_MCI_FIRST) &&
  1883.                 (uMessage <= DRV_MCI_LAST))
  1884.             {
  1885.                 return (viscaMciProc(LOWORD(dwDriverID), (WORD)uMessage, lParam1, lParam2));
  1886.             }
  1887.             else
  1888.             {
  1889.                 /* other messages get default processing */
  1890.                 return (DefDriverProc(dwDriverID, hDriver, uMessage, lParam1, lParam2));
  1891.             }
  1892.     }                  
  1893.     return (0L);
  1894. }
  1895.